﻿/*******************************************************************************
 * Copyright © 2016 DJY 版权所有
 * Author: DJY
 * Description: 后台管理系统
 * Website：http://www.djyweb.cn
*********************************************************************************/
using DJY.Code;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.RegularExpressions;

namespace DJY.Data
{
    /// <summary>
    /// 仓储实现
    /// </summary>
    public abstract class DbRepository<TEntity> : IDbRepository<TEntity> where TEntity : class, new()
    {
        /// <summary>
        /// 插入单个实体
        /// </summary>
        /// <param name="dbcontext"></param>
        /// <param name="entity"></param>
        /// <returns></returns>
        public int Insert(DbContext dbcontext,TEntity entity)
        {
            dbcontext.Entry<TEntity>(entity).State = EntityState.Added;
            return dbcontext.SaveChanges();
        }

        /// <summary>
        /// 插入多个实体
        /// </summary>
        /// <param name="dbcontext"></param>
        /// <param name="entitys"></param>
        /// <returns></returns>
        public int Insert(DbContext dbcontext, List<TEntity> entities)
        {
            foreach (var entity in entities)
            {
                dbcontext.Entry<TEntity>(entity).State = EntityState.Added;
            }
            return dbcontext.SaveChanges();
        }

        /// <summary>
        /// 更新单个实体
        /// </summary>
        /// <param name="dbcontext"></param>
        /// <param name="columns">指定列</param>
        /// <param name="entity"></param>
        /// <returns></returns>
        public int Update(DbContext dbcontext,ICollection<string> columns, TEntity entity)
        {
            PropertyInfo[] props = entity.GetType().GetProperties();
            if (columns != null && columns.Count > 0)
            {
                props = props.Where(p => p.CanWrite && columns.Contains(p.Name, StringComparer.OrdinalIgnoreCase)).ToArray();

                if (props.Length == 0)
                    return 0;
            }

            dbcontext.Set<TEntity>().Attach(entity);
            var dbEntry = dbcontext.Entry(entity);
            foreach (PropertyInfo prop in props)
            {
                if (!prop.CanWrite)
                    continue;

                object v = prop.GetValue(entity, null);
                if (v != null && v.ToString().Equals("&nbsp;"))
                {
                    dbEntry.Property(prop.Name).CurrentValue = null;
                }
                dbEntry.Property(prop.Name).IsModified = true;
            }

            return dbcontext.SaveChanges();
        }

        /// <summary>
        /// 更新多个实体，未使用事务
        /// </summary>
        /// <param name="dbcontext"></param>
        /// <param name="columns">指定列</param>
        /// <param name="entities"></param>
        /// <returns></returns>
        public int Update(DbContext dbcontext, ICollection<string> columns, List<TEntity> entities)
        {
            PropertyInfo[] props = typeof(TEntity).GetProperties();
            if(columns != null && columns.Count > 0)
            {
                props = props.Where(p => p.CanWrite && columns.Contains(p.Name, StringComparer.OrdinalIgnoreCase)).ToArray();

                if (props.Length == 0)
                    return 0;
            }

            DbSet<TEntity> dbSet = dbcontext.Set<TEntity>();
            foreach (TEntity entity in entities)
            {
                dbSet.Attach(entity);
                var dbEntry = dbcontext.Entry(entity);
                foreach (PropertyInfo prop in props)
                {
                    if (!prop.CanWrite)
                        continue;

                    object v = prop.GetValue(entity, null);
                    if (v != null && prop.PropertyType == typeof(string) && (string)v == "&nbsp;")
                    {
                        dbEntry.Property(prop.Name).CurrentValue = null;
                    }

                    dbEntry.Property(prop.Name).IsModified = true;
                }
            }

            return dbcontext.SaveChanges();
        }

        /// <summary>
        /// 删除单个实体
        /// </summary>
        /// <param name="dbcontext"></param>
        /// <param name="entity"></param>
        /// <returns></returns>
        public int Delete(DbContext dbcontext, TEntity entity)
        {
            dbcontext.Set<TEntity>().Attach(entity);
            dbcontext.Entry<TEntity>(entity).State = EntityState.Deleted;
            return dbcontext.SaveChanges();
        }

        /// <summary>
        /// 删除单个实体
        /// </summary>
        /// <param name="dbcontext"></param>
        /// <param name="entity"></param>
        /// <returns></returns>
        public int Delete(DbContext dbcontext, List<TEntity> entities)
        {
            var set = dbcontext.Set<TEntity>();
            foreach (var entity in entities)
            {
                set.Attach(entity);
                dbcontext.Entry<TEntity>(entity).State = EntityState.Deleted;
            }
            return dbcontext.SaveChanges();
        }

        /// <summary>
        /// 删除多个实体
        /// </summary>
        /// <param name="dbcontext"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public int Delete(DbContext dbcontext, Expression<Func<TEntity, bool>> predicate)
        {
            DbSet<TEntity> dbSet = dbcontext.Set<TEntity>();
            var entitys = dbSet.Where(predicate).ToList();
            dbSet.RemoveRange(entitys);
            //entitys.ForEach(m => dbcontext.Entry<TEntity>(m).State = EntityState.Deleted);
            return dbcontext.SaveChanges();
        }

        public TEntity FindEntity(DbContext dbcontext, 
            Expression<Func<TEntity, bool>> predicate)
        {
            return dbcontext.Set<TEntity>().FirstOrDefault(predicate);
        }

        public IQueryable<TEntity> IQueryable(DbContext dbcontext)
        {
            return dbcontext.Set<TEntity>();
        }

        public IQueryable<TEntity> IQueryable(DbContext dbcontext, 
            Expression<Func<TEntity, bool>> predicate)
        {
            return dbcontext.Set<TEntity>().Where(predicate);
        }

        public List<TEntity> FindList(DbContext dbcontext, string strSql)
        {
            return dbcontext.Database.SqlQuery<TEntity>(strSql).ToList<TEntity>();
        }

        public List<TEntity> FindList(DbContext dbcontext, string strSql, DbParameter[] dbParameter)
        {
            return dbcontext.Database.SqlQuery<TEntity>(strSql, dbParameter).ToList<TEntity>();
        }

        public List<TEntity> FindList(DbContext dbcontext, Pagination pagination)
        {
            bool isAsc = pagination.sord.ToLower() == "asc" ? true : false;
            string[] _order = pagination.sidx.Split(',');
            MethodCallExpression resultExp = null;
            var tempData = dbcontext.Set<TEntity>().AsQueryable();
            foreach (string item in _order)
            {
                string _orderPart = item;
                _orderPart = Regex.Replace(_orderPart, @"\s+", " ");
                string[] _orderArry = _orderPart.Split(' ');
                string _orderField = _orderArry[0];
                bool sort = isAsc;
                if (_orderArry.Length == 2)
                {
                    isAsc = _orderArry[1].ToUpper() == "ASC" ? true : false;
                }
                var parameter = Expression.Parameter(typeof(TEntity), "t");
                var property = typeof(TEntity).GetProperty(_orderField);
                var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                var orderByExp = Expression.Lambda(propertyAccess, parameter);
                resultExp = Expression.Call(typeof(Queryable), isAsc ? "OrderBy" : "OrderByDescending", new Type[] { typeof(TEntity), property.PropertyType }, tempData.Expression, Expression.Quote(orderByExp));
            }
            tempData = tempData.Provider.CreateQuery<TEntity>(resultExp);
            pagination.records = tempData.Count();
            tempData = tempData.Skip<TEntity>(pagination.rows * (pagination.page - 1))
                .Take<TEntity>(pagination.rows).AsQueryable();
            return tempData.ToList();
        }

        public List<TEntity> FindList(DbContext dbcontext, 
            Expression<Func<TEntity, bool>> predicate, 
            Pagination pagination)
        {
            bool isAsc = pagination.sord.ToLower() == "asc" ? true : false;
            string[] _order = pagination.sidx.Split(',');
            MethodCallExpression resultExp = null;
            var tempData = dbcontext.Set<TEntity>().Where(predicate);
            foreach (string item in _order)
            {
                string _orderPart = item;
                _orderPart = Regex.Replace(_orderPart, @"\s+", " ");
                string[] _orderArry = _orderPart.Split(' ');
                string _orderField = _orderArry[0];
                bool sort = isAsc;
                if (_orderArry.Length == 2)
                {
                    isAsc = _orderArry[1].ToUpper() == "ASC" ? true : false;
                }
                var parameter = Expression.Parameter(typeof(TEntity), "t");
                var property = typeof(TEntity).GetProperty(_orderField);
                var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                var orderByExp = Expression.Lambda(propertyAccess, parameter);
                resultExp = Expression.Call(typeof(Queryable), isAsc ? "OrderBy" : "OrderByDescending", new Type[] { typeof(TEntity), property.PropertyType }, tempData.Expression, Expression.Quote(orderByExp));
            }
            tempData = tempData.Provider.CreateQuery<TEntity>(resultExp);
            pagination.records = tempData.Count();
            tempData = tempData.Skip<TEntity>(pagination.rows * (pagination.page - 1)).Take<TEntity>(pagination.rows).AsQueryable();
            return tempData.ToList();
        }
    }
}
